#include "Dungeon.h"
#include "Player.h"
#include "Game.h"
#include "Portal.h"
#include "CALevel.h"
#include "PseudoClassicLevel.h"
#include "Util.h"
#include "Tile.h"
#include "Misc.h"
#include "Door.h"
#include "Wall.h"
#include "Npc.h"
#include "StringLib.h"
#include "GameState.h"

Dungeon::Dungeon(DungeonType dungeonType, int numLevels, int dLevel) {
	m_dungeonType = dungeonType;
	m_numLevels = numLevels;
	m_dLevel = dLevel;
	m_levels.resize(m_numLevels);
	for(int i = 0; i < m_numLevels; i++)
		m_levels[i] = NULL;
}

Dungeon::Dungeon(std::ifstream& file) {
	std::string line;
	std::getline(file, line);
	int* tokens = Util::intSplit(line);
	m_dungeonType = DungeonType(tokens[0]);
	m_numLevels = tokens[1];
	m_dLevel = tokens[2];
	m_levels.resize(m_numLevels);
	for(int i = 0; i < m_numLevels; i++)
		m_levels[i] = NULL;
	int numActualLevels = tokens[3];
	for(int i = 0; i < numActualLevels; i++)
		m_levels[i] = new Level(this, file);
	delete[] tokens;
}

Dungeon::~Dungeon() {
	for(int i = 0; i < m_numLevels; i++)
		if(m_levels[i] != NULL) delete m_levels[i];
	m_levels.clear();
}

void Dungeon::save(std::ofstream& file) const {
	file << m_dungeonType << " " << m_numLevels << " " << m_dLevel << " ";
	int numActualLevels = 0;
	for(int i = 0; i < m_numLevels; i++)
		if(m_levels[i] != NULL) numActualLevels++;
	file << numActualLevels << "\n";
	for(int i = 0; i < m_numLevels; i++)
		if(m_levels[i] != NULL) m_levels[i]->save(file);
}

void Dungeon::enterDungeon(Player* player) {
	g_currentDungeon = this;
	getLevel(0)->placePlayer(player, true, 0);
}

Level* Dungeon::getLevel(int levelNum) {
	if(levelNum < 0 || levelNum >= m_numLevels) return NULL;
	if(m_levels[levelNum] == NULL) {
		if(levelNum == 0) {
			if(one_in(2))
				m_levels[levelNum] = new PseudoClassicLevel(COLS, ROWS, 0, m_dLevel, this);
			else
				m_levels[levelNum] = new CALevel(COLS, ROWS, 0, m_dLevel, this);
		} else {
			if(one_in(2))
				m_levels[levelNum] = new PseudoClassicLevel(COLS, ROWS, m_levels[levelNum-1]);
			else
				m_levels[levelNum] = new CALevel(COLS, ROWS, m_levels[levelNum-1]);
		}
		if(levelNum == m_numLevels-1) {
			bool safe = false;
			int randX, randY;
			for(int i = 0; i < 100 && !safe; i++) {
				randX = rand_plus_one(COLS-4);
				randY = rand_plus_one(ROWS-5)+2;
				if(randY-2 <= 0 || !m_levels[levelNum]->getTile(randX, randY-2)->isEmpty()) continue;
				safe = true;
				for(int j = -1; j <= 1; j++) {
					for(int k = -1; k <= 2; k++) {
						if(!m_levels[levelNum]->getTile(randX+j, randY+k)->isEmpty()) {
							safe = false;
							break;
						}
					}
					if(!safe) break;
				}
			}
			if(safe) {
				for(int j = -1; j <= 1; j++) {
					for(int k = -1; k <= 2; k++) {
						if(j == 0 && k == 1) {
							m_levels[levelNum]->getTile(randX, randY+k)->addItem(new Misc(MT_VICTORY_ITEM));
						} else if(j == 0 && k == -1) {
							m_levels[levelNum]->getTile(randX, randY+k)->addObstacle(new Door(LockType(rand(NUM_LOCK_TYPES)), one_in(2)));
						} else if(j == 0 && k == 0) {
							m_levels[levelNum]->addNpc(new Npc(NT_THRASHING_HORROR), randX, randY+k);
						} else {
							m_levels[levelNum]->getTile(randX+j, randY+k)->addObstacle(new Wall());
						}
					}
				}
			} else {
				while(true) {
					randX = rand_plus_one(COLS-2);
					randY = rand_plus_one(ROWS-2);
					if(!m_levels[levelNum]->getTile(randX, randY)->isOccupied())
						m_levels[levelNum]->getTile(randX, randY)->addItem(new Misc(MT_VICTORY_ITEM));
				}
			}
		}
	}
	return m_levels[levelNum];
}

void Dungeon::takePortal(Player* player, Portal* portal) {
	if(portal->isUp() && portal->getLevel()->getLevelNum() == 0) {
		if(player->inventoryContains(TH_MISC_TYPE, MT_VICTORY_ITEM)) {
			g_textLog->addLine(g_stringLib->getTextString(ST_PLAYER_ASCEND_SUCCESS, g_stringLib->getObjectString(TH_MISC_TYPE, MT_VICTORY_ITEM).c_str()));
			g_gameMode.push(GM_DEAD);
		} else {
			g_textLog->addLine(g_stringLib->getTextString(ST_PLAYER_ASCEND_FAIL, g_stringLib->getObjectString(TH_MISC_TYPE, MT_VICTORY_ITEM).c_str()));
		}
	}
}
